package agents.constructor;


import objects.residencia.*;
import objects.suelo.*;
import agents.persona.*;
import agents.Agentes;

import repast.simphony.context.Context;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.parameter.Parameters;
import repast.simphony.util.ContextUtils;
import repast.simphony.space.grid.*;

import java.util.*;


/*********************************************************************************************************************
 * Estudia el Suelo y determina donde construir Residencias Ricas. Redefine algunos corportaminetos heredados.
 * Este Agente  tiene prioridad de construccion sobre los Constructores de Residencias Pobres pero no sobre los
 * de Centros de Trabajo.
 * 
 * @author Henry Alberto
 * @author Fedrico Lievano
 */

public class ConstructorResidenciaRica extends Constructor {
	
    protected static long agentIDCounter = 1;
    protected String agentID = "Constructura de Residencias Ricas " + (agentIDCounter++);
    
    /*******************************************************************************************************************
	 * Constructores  de la clase. Es posible crear Constructores de Residencias Ricas con sus propiedades en 
	 * nulo o con algun valor en particular.
	 */
	public ConstructorResidenciaRica(double utilidad, double beneficio){
		this.utilidad = utilidad;
		this.beneficioMonetario = beneficio;
	}
	
	public ConstructorResidenciaRica(){
		this.utilidad = 0;
		this.beneficioMonetario = 0;
	}
	
	/****************************************************************************************************************
	 * Este metodo es heredado y sobreescrito. Obtiene el nivel de utilidad para el Constructor de Residencias Ricas  
	 * en base al Suelo que este estudiando en la pre-construccion.
	 */
	@Override
	public double getUtilidad(Suelo mejor){
		//Crea un canal de comunicacion entre los parametros editables del modelo y el codigo
		Parameters p = RunEnvironment.getInstance().getParameters();
		
		//Se obtienen los niveles de preferencias para los Constructores de Residencias Ricas
		double alpha = (Double)p.getValue("alphaResidenciaRica");
		double beta = (Double)p.getValue("betaResidenciaRica");
		double gama = (Double)p.getValue("gamaResidenciaRica");
		
		//Se obtiene valores de algunas de las propiedades del Suelo en estudio
		double claseAlta = mejor.getClaseAlta();
		double densidad = mejor.getDensidad();
		double distancia = mejor.getDistancia();
		
		distancia = distancia / 200;
		
		claseAlta = claseAlta/3;
		
		//Se calcula el nivel de utilidad en base a esta funcion de Cobb-Douglas y el uso de los parametros antes almacenados.
		double utilidad = Math.pow((100/distancia),alpha) + Math.pow((densidad), beta) + Math.pow(claseAlta, gama);
		
		return utilidad;
	}
	

	/****************************************************************************************************************
	 * Este comportamiento es heredado y sobreescrito.
	 * Acomula un valor a su nivel neto de beneficios. 
	 * Dicho valor a sumar, depende de  la capacidad del sitio construido.
	 */
	@Override
	public void setBeneficio(int capacidad){
		beneficioMonetario = beneficioMonetario + 60 * capacidad;
	}
	
	/********************************************************************************************************************
	 * Recibe como argumento el Suelo sobre el cual se va a Construir la nueva Residencia Rica
	 */
	@Override
	public void Construir(SueloRural mejor){
		/******************************************************************************************/
		Context context = ContextUtils.getContext(this);			//Referencia al contexto del modelo
		Grid patch = (Grid) context.getProjection("Medellin Grid"); //Referencia a la malla del modelo.
		/******************************************************************************************/
		
		/******************************************************************************************/
		GridPoint point = patch.getLocation(mejor);					//Localizacion del Suelo sobre el que se construira.
		
		int x = point.getX();   //Cordenada x del Suelo sobre el que se construira.
		int y = point.getY();   //Cordenada y del Suelo sobre el que se construira.
		/******************************************************************************************/
				
		/******************************************************************************************/
		SueloUrbano nuevoSuelo = new SueloUrbano(mejor); //El Suelo construido pasa se ser Rural a Urbano
		
		//Para lleva a cabo este cambio, se debe eliminar el objeto SueloRural y crear un nuevo objeto tipo 
		//SueloUrbano con las propiedades y localizacion del objeto eliminado.
		
		context.remove(mejor);          //Desaparece el Suelo Rural
		context.add(nuevoSuelo); 	    //Aparece un nuevo Suelo Urbano
		patch.moveTo(nuevoSuelo, x, y); //Se ubica dondde estaba el Suelo Rural anteriormente eliminado.
		/******************************************************************************************/
			
		/******************************************************************************************/
		ResidenciaRica nuevaResidencia = new ResidenciaRica(); //Genera un nuevo Objeto Residencia Rica
		
		context.add(nuevaResidencia);//Se introduce al modelo
		patch.moveTo(nuevaResidencia, x, y);//Se ubica la nueva Residencia Rica en la pocision seleccionada por el Agente Constructor de Centro de Trabajo
		Agentes.gobierno.setAhorro(Agentes.gobierno.getAhorro() - 600); //Se gasta 80 del ahorro del Gobierno
		/******************************************************************************************/
				
		/******************************************************************************************/
		asignarResidencia(nuevaResidencia);
		getPoblacionResRicas();
		
		Agentes.gobierno.setPersonasRicasDesempleadasReal(1);
		Agentes.gobierno.setPersonasPobresDesempleadasReal(5);
		/******************************************************************************************/
		
		/******************************************************************************************/
		setEstadoConstruccion(true); //Cambia el estado y se disparan Valorizar()
		
		nuevoSuelo.ValorizarResidenciaRica(context);
		
		setBeneficio(50);
		/******************************************************************************************/
		
		/******************************************************************************************/
		//Actualiza la propiedad de "densidad" de cada objeto Suelo
		for (Object o : patch.getObjects())
		{
			if (o instanceof SueloUrbano || o instanceof SueloRural)
				((Suelo)o).stepDensidad(context);
				
		}
		/******************************************************************************************/
		
		double utilidadCT;
		double utilidadCP;
		double utilidadCR;
		
		for (Object parcialaux : patch.getObjects())
		{		
				if(parcialaux instanceof SueloRural)
				{ 
					utilidadCT = Agentes.constructorCentroTrabajo.getUtilidad((Suelo) parcialaux);
					((Suelo) parcialaux).setUtilidadCTInicial(utilidadCT);
					
					utilidadCP = Agentes.constructorResidenciaPobre.getUtilidad((Suelo) parcialaux);
					((Suelo) parcialaux).setUtilidadCPInicial(utilidadCP);
					
					utilidadCR = getUtilidad((Suelo) parcialaux);
					((Suelo) parcialaux).setUtilidadCRInicial(utilidadCR);
				}

		}
			
		Agentes.gobierno.LicenciarConstruccionResRicas();
	}

	/**************************************************************************************************************
	 * Se buscan las Personas Ricas que viven arrendadas y que esten empleadas. Para comprar una Residencia Rica
	 */
	 public void asignarResidencia(ResidenciaRica nuevaResidencia){
	 
		 	//Tener referencia al contento sobre el cual se corre el modelo
			Context context = ContextUtils.getContext(this);
					
			//Se referencia la malla donde se dibujo el comportamiento de los agentes.
			Grid patch = (Grid) context.getProjection("Medellin Grid");
			
			/******************************************************************************************/
			ArrayList<Persona>	ricosArrendados = new ArrayList<Persona>();
			for (Object parcial : patch.getObjects())//Se obtienen los elementos ubicados en el Modelo
			{
				if (parcial instanceof PersonaRica)//Si el elemento k es de tipo Persona Rica					
				{
					//Si la Persona Rica de la posicion k esta Arrendada y Empleada
					if(((Persona)parcial).getResidencia() == null && ((Persona)parcial).isEmpleada()) 
							ricosArrendados.add((PersonaRica)parcial); //Se almacena en el arreglo de Personas Ricas Arrendadas
				}
			}
			/******************************************************************************************/	
			
			/******************************************************************************************/
			int n = 0;
			for(int i = 0; (i<3 && i<ricosArrendados.size()); i++)
			{
				n++;
				ricosArrendados.get(i).setResidencia(nuevaResidencia);
			}
			/******************************************************************************************/
			
			//Gobierno debe analizar nuevamente las Licencias
			Agentes.gobierno.setPersonasRicasResidencia(Agentes.gobierno.getPersonasRicasResidencia() + n);
	}
	
	@Override
	public boolean isResidenciaRica(){
		return true;
	}
	
	@Override
	public String toString(){
		return agentID;
	}
	
	 public double getPoblacionResRicas(){
			
		   
			double PRR =  (Agentes.gobierno.getPersonasRicasResidencia()); 
			
			
			return PRR;
		}
}
